/**
 * ImageApplication.java
 * Author: 
 * Description:
 * 	Main application class.  This application
 * 	implements 3 basic image operations:
 * 	- threshold
 * 	- image negative
 * 	- contrast stretching
 *
 * 	Code based of same code provided by Prof. Grewe.
 */

import java.awt.*;
import java.util.*;

public class ImageApplication
{
	// This uses generics as the old vector use (non-generics)
	// is deprecated.  This is similar to templates in C++.
	private Vector<IPWindow> windows;

	public ImageApplication()
	{
		windows = new Vector<IPWindow>();
		createWindow();
	}

	////
	// Create window without any image loaded.
	public void createWindow()
	{
		// Create window and add to our list
		IPWindow ipwin = new IPWindow(this);
		windows.add(ipwin);

		// Extract index of this window and set it
		int winid = windows.indexOf(ipwin); 
		ipwin.setWindowId(winid);

		p.print("created window: " + winid);
	}

	////
	// Create window using specified image and ROI
	public void createWindow(ImageData img, int x, int y, int w, int h)
	{
		// Create window and add to our list
		IPWindow ipwin = new IPWindow(this, img, x, y, w, h);
		windows.add(ipwin);

		// Extract index of this window and set it
		int winid = windows.indexOf(ipwin); 
		ipwin.setWindowId(winid);

		p.print("created window: " + winid);
	}

	////
	// Call to close a specified window.
	public void closeWindow(int windowID)
	{
		p.print("Closing window: " + windowID);

		// Notify window it is closing
		IPWindow win = windows.elementAt(windowID);
		win.notifyWindowIsClosing();

		// Remove window from this list.
		windows.remove(windowID);

		// Reassign window ids
		reassignWindowIDs();

		// Exit if no windows left.
		if (windows.size() < 1)
		{
			System.exit(0);
		}
	}

	////
	// Reassigns window ids to the windows.
	// This is necessary when removing an element in the middle
	// of the list, as indicies will change due to elements
	// being shifted.
	public void reassignWindowIDs()
	{
		for (int i = 0; i < windows.size(); i++)
		{
			IPWindow win = windows.elementAt(i);
			win.setWindowId(i);
		}
	}

	public void threshold(int tvalue, IPWindow win)
	{
		p.print("Applying threshold value: " + tvalue);

		ImageData new_image = win.getActiveImage().threshold(tvalue);
		win.setActiveImage(new_image);
	}

	public void imageNegative(IPWindow win)
	{
		p.print("Applying image negative");

		// Get pixels
		int width = win.getActiveImage().getWidth();
		int height = win.getActiveImage().getHeight();
		int [] pixels = new int[width * height];
		win.getActiveImage().getPixels(pixels, 0, 0,
				width, height);

		// Modify pixels.
		for (int y=0; y < height; y++)
		{
			for (int x=0; x < width; x++)
			{
				// Extract rgb
				int pixel = pixels[y*width + x];
				int r = (pixel >> 16) & 0xff;
				int g = (pixel >> 8) & 0xff;
				int b = pixel & 0xff;

				// Perform image negative.
				r = 255 - r;
				g = 255 - g;
				b = 255 - b;

				// Reform rgb
				pixel = Util.setRGB(r, g, b);
				pixels[y*width + x] = pixel;
			}
		}

		// Set pixels
		win.getActiveImage().setPixels(pixels, 0, 0,
				width, height);
	}

	public void contrastStretch(IPWindow win)
	{
		p.print("Applying contrast stretch");

		// Get pixels
		int width = win.getActiveImage().getWidth();
		int height = win.getActiveImage().getHeight();
		int [] pixels = new int[width * height];
		win.getActiveImage().getPixels(pixels, 0, 0,
				width, height);

		int lowr, lowg, lowb;
		int hir, hig, hib;
		lowr = lowg = lowb = 255;
		hir = hig = hib = 0;

		// 1st pass is to record low/high values.
		for (int y=0; y < height; y++)
		{
			for (int x=0; x < width; x++)
			{
				// Extract rgb
				int pixel = pixels[y*width + x];
				int r = (pixel >> 16) & 0xff;
				int g = (pixel >> 8) & 0xff;
				int b = pixel & 0xff;

				// Record lowest/hightest rgb
				if (r < lowr) lowr = r;
				if (r > hir) hir = r;
				if (g < lowg) lowg = g;
				if (g > hig) hig = g;
				if (b < lowb) lowb = b;
				if (b > hib) hib = b;

				// Reform rgb
				pixel = 0xFF000000 | r << 16 | g << 8 | b;
				pixels[y*width + x] = pixel;
			}
		}

		// 2nd pass is to perform scaling.
		for (int y=0; y < height; y++)
		{
			for (int x=0; x < width; x++)
			{
				// Extract rgb
				int pixel = pixels[y*width + x];
				int r = (pixel >> 16) & 0xff;
				int g = (pixel >> 8) & 0xff;
				int b = pixel & 0xff;

				// Perform normilization
				// http://homepages.inf.ed.ac.uk/rbf/HIPR2/stretch.htm
				r = (r - lowr) * ( (255 - 0) / (hir - lowr) ) + 0;
				g = (g - lowg) * ( (255 - 0) / (hig - lowg) ) + 0;
				b = (b - lowb) * ( (255 - 0) / (hib - lowb) ) + 0;

				// Reform rgb
				pixel = 0xFF000000 | r << 16 | g << 8 | b;
				pixels[y*width + x] = pixel;
			}
		}

		// Set pixels
		win.getActiveImage().setPixels(pixels, 0, 0,
				width, height);
	}

	static public void main(String args[])
	{
		try
		{
			new ImageApplication();
		}
		catch (Throwable t)
		{
			System.err.println(t);
			t.printStackTrace();
			//Ensure the application exits with an error condition.
			System.exit(1);
		}
	}
}

